package com.kaifamiao.util;

import com.kaifamiao.annotation.Select;
import com.kaifamiao.model.Configuration;
import com.kaifamiao.model.Mapper;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 用来解析 xml 配置文件
 */
public class XMLConfigBuilder {

    /**
     * 解析配置文件
     * @param inputStream 配置文件的输入流
     * @return
     */
    public static Configuration loadConfiguration(InputStream inputStream) {
        // 创建 Configuration 对象
        Configuration configuration = new Configuration();
        try {
            // 解析 xml
            SAXReader saxReader = new SAXReader();
            // xml 文档信息
            Document document = saxReader.read(inputStream);
            // 获取根节点
            Element rootElement = document.getRootElement();
            // 获取 db.properties 的配置信息
            Element propertiesElement = rootElement.element("properties");
            // 获取路径
            String resourcePath = propertiesElement.attributeValue("resource");
            // 获取 db.properties 配置文件的输入流
            InputStream resourceAsStream = XMLConfigBuilder.class.getClassLoader().getResourceAsStream(resourcePath);
            Properties pro = new Properties();
            pro.load(resourceAsStream);
            // 查找 property 节点
            List<Element> properties = rootElement.selectNodes("//property");

            // 解析封装到 configuration 实体对象中
            for (Element property : properties) {
                String name = property.attributeValue("name");
                String value = pro.getProperty(name);
                switch (name) {
                    case "driver":
                        configuration.setDriver(value);
                        break;
                    case "url":
                        configuration.setUrl(value);
                        break;
                    case "username":
                        configuration.setUsername(value);
                        break;
                    case "password":
                        configuration.setPassword(value);
                        break;
                }
            }

            // 解析 mappers
            List<Element> mappers = rootElement.selectNodes("//mapper");
            for (Element mapper : mappers) {
                String resource = mapper.attributeValue("resource");
                String aClass = mapper.attributeValue("class");
                if (null != resource) {
                    // 说明使用的是 xml 映射文件
                    System.out.println("写的是 xml 映射文件");
                    loadMapper(resource, configuration);
                } else if (null != aClass) {
                    // 说明使用的是注解
                    loadAnnotation(aClass, configuration);
                    System.out.println("使用的是注解");
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return configuration;
    }

    /**
     * 解析注解
     * @param aClass
     * @param configuration
     */
    private static void loadAnnotation(String aClass, Configuration configuration) {
        // 创建 map
        Map<String, Mapper> mappers = new HashMap<>();
        try {
            // 先获取 Class 对象
            Class clazz = Class.forName(aClass);
            // 获取所有方法
            Method[] methods = clazz.getDeclaredMethods();
            // 迭代封装到实体中
            for (Method method : methods) {
                // 获取方法名
                String methodName = method.getName();
                // 获取要封装成什么类型
                String resultType = "";
                // 获取返回值类型 （泛型的类型)
                Type genericReturnType = method.getGenericReturnType();
                // 判断是否是参数化类型
                if (genericReturnType instanceof ParameterizedType) {
                    // 强转出来，获取真实参数类型
                    Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                    for (Type actualTypeArgument : actualTypeArguments) {
                        // 泛型的返回值类型
                        resultType = actualTypeArgument.getTypeName();
                    }
                }
                // 通过反射获取注解
                Select annotation = method.getAnnotation(Select.class);
                // 获取 sql
                String sql = annotation.value();
                // 创建 Mapper
                Mapper mapper = new Mapper();
                // 将信息封装到 mapper
                mapper.setSql(sql);
                mapper.setResultType(resultType);
                // 放到 mappers 中
                mappers.put(aClass + "." + methodName, mapper);
            }
            // 存到 configuration 中
            configuration.setMappers(mappers);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析 mapper 映射文件
     * @param resource 映射文件 mapper 的位置
     * @param configuration 用来封装信息的对象
     */
    private static void loadMapper(String resource, Configuration configuration) {
        // 创建 map
        Map<String, Mapper> mappers = new HashMap<>();
        try {
            // 获取 xml 映射文件的数据流
            InputStream resourceAsStream = XMLConfigBuilder.class.getClassLoader().getResourceAsStream(resource);
            // 解析 xml
            SAXReader saxReader = new SAXReader();
            // xml 文档信息
            Document document = saxReader.read(resourceAsStream);
            // 获取根节点 mapper
            Element rootElement = document.getRootElement();
            // 获取 namespace 绑定的接口
            String namespace = rootElement.attributeValue("namespace");
            // 查找 select
            List<Element> selectNodes = rootElement.selectNodes("//select");

            // 迭代封装到实体中
            for (Element selectNode : selectNodes) {
                Mapper mapper = new Mapper();
                // 获取 id 属性值
                String id = selectNode.attributeValue("id");
                // 获取 resultType 属性值
                String resultType = selectNode.attributeValue("resultType");
                // 获取 sql
                String sql = selectNode.getTextTrim();
                // 封装到 mapper 中
                mapper.setResultType(resultType);
                mapper.setSql(sql);
                // 添加到 mappers 集合中
                mappers.put(namespace + "." + id, mapper);
            }
            // 存到 configuration 中
            configuration.setMappers(mappers);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Configuration configuration = loadConfiguration(XMLConfigBuilder.class.getClassLoader().getResourceAsStream("SqlMapConfig2.xml"));
        System.out.println(configuration);
    }
}
